/*
 * Created by Spear (http://www.codernet.es), spear@codernet.es
 * Coordinator of XNA Community (http://www.codeplex.com/XNACommunity)
 * 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using System.ComponentModel;

using XMas;

namespace XMasContent
{
    [ContentProcessor(DisplayName = "XMAS - MD5Process")]
    public class MD5Process : ModelProcessor
    {
        #region Fields

        CSkeletonData  skeleton_data;
        private string directory = "";
        private bool   bIncludeShader = true;
        private string modelTextureName;
        //Vector3[] all_list_positions = null;

        #endregion


        #region Properties

        [DisplayName("Model Texture")]
        [Description("This file is texture model datal.")]
        [DefaultValue("")]
        [Browsable(true)]
        public string ModelTextureName
        {
            get { return modelTextureName; }
            set { modelTextureName = value; }
        }

        [DisplayName("Include shader and texture")]
        [Description("Set to true if you can include shader and texture into model automatly.")]
        [DefaultValue(true)]
        [Browsable(true)]
        public bool IncludeShader
        {
            get { return bIncludeShader; }
            set { bIncludeShader = value; }
        }

        #endregion


        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            //System.Diagnostics.Debugger.Launch();

            skeleton_data = new CSkeletonData();
            if (input == null)
                throw new ArgumentNullException("input");

            // Load textures
            directory = Path.GetDirectoryName(input.Identity.SourceFilename);
            LookUpNormalMapAndAddToTextures(input);

            //Bind skeleton
            BoneContent bind_skeleton = MeshHelper.FindSkeleton(input);
            ModelContent model = base.Process(input, context);
            XmasContent.GetInstance().warn("\n\nBind skeleton:", false);
            IList<BoneContent> bind_bones = MeshHelper.FlattenSkeleton(bind_skeleton);
            foreach (BoneContent bone in bind_bones)
                skeleton_data.bind_skeleton.Add(bone.Transform);
            
            // Load animations
            string[] md5anim_files = Directory.GetFiles(directory, "*.md5anim");
            for (int i = 0; i < md5anim_files.Length; ++i)
            {
                CMD5AnimParser anim_parser = new CMD5AnimParser();
                anim_parser.load(md5anim_files[i], skeleton_data);
            }

            
            model.Tag = skeleton_data;
            return model;
        }

    
        private void LookUpNormalMapAndAddToTextures(NodeContent node)
        {
            MeshContent mesh = node as MeshContent;
            if (mesh != null)
            {
                bool has_normal_map = false;
                bool has_height_map = false;
                bool has_specular_map = false;
                string normal_filename = "", heightmap_filename = "", specular_filename = "";
                if (!String.IsNullOrEmpty(NormalMapTexture))
                {
                    normal_filename = Path.GetFullPath(Path.Combine(directory, NormalMapTexture));
                    if (File.Exists(normal_filename))
                    {
                        has_normal_map = true;
                    }
                    else
                    {
                        throw new ArgumentNullException("Normal Map Texture not exist '" + normal_filename + "'");
                    }
                }
                if (!String.IsNullOrEmpty(SpecularMapTexture))
                {
                    specular_filename = Path.GetFullPath(Path.Combine(directory, SpecularMapTexture));
                    if (File.Exists(specular_filename))
                    {
                        has_specular_map = true;
                    }
                    else
                    {
                        throw new ArgumentNullException("Specular Map Texture not exist '" + specular_filename + "'");
                    }
                }
                if (!String.IsNullOrEmpty(HeightMapTexture))
                {
                    heightmap_filename = Path.GetFullPath(Path.Combine(directory, HeightMapTexture));
                    if (File.Exists(heightmap_filename))
                    {
                        has_height_map = true;
                    }
                    else
                    {
                        throw new ArgumentNullException("Height Map Texture not exist '" + heightmap_filename + "'");
                    }
                }

                foreach (GeometryContent geometry in mesh.Geometry)
                {
                    if (has_normal_map)
                    {
                        geometry.Material.Textures.Add("NormalMap",
                        new ExternalReference<TextureContent>(normal_filename));
                    }
                    if (has_height_map)
                    {
                        geometry.Material.Textures.Add("HeightMap",
                        new ExternalReference<TextureContent>(heightmap_filename));
                    }
                    if (has_specular_map)
                    {
                        geometry.Material.Textures.Add("SpecularMap",
                        new ExternalReference<TextureContent>(specular_filename));
                    }
               }
            }
            foreach (NodeContent child in node.Children)
            {
                LookUpNormalMapAndAddToTextures(child);
            }
        }

    
        protected override MaterialContent ConvertMaterial(MaterialContent material, ContentProcessorContext context)
        {
            /*
            string d = directory.ToLower();
            if (d.IndexOf("content") > 0)
            {
                int pos = d.IndexOf("content");
                d = d.Substring(0, pos) + "content";
            }
            EffectMaterialContent normalMappingMaterial = new EffectMaterialContent();
            normalMappingMaterial.Effect = new ExternalReference<EffectContent>
                (Path.Combine(d, "SkinnedModel.fx"));

            // copy the textures in the original material to the new normal mapping
            // material. this way the diffuse texture is preserved. The
            // PreprocessSceneHierarchy function has already added the normal map
            // texture to the Textures collection, so that will be copied as well.
            foreach (KeyValuePair<String, ExternalReference<TextureContent>> texture
                in material.Textures)
            {
                normalMappingMaterial.Textures.Add(texture.Key, texture.Value);
            }

            // and convert the material using the NormalMappingMaterialProcessor,
            // who has something special in store for the normal map.
            return context.Convert<MaterialContent, MaterialContent>
                (normalMappingMaterial, typeof(MaterialProcessor).Name);
             */

            //if (!bIncludeShader)
            //    return base.ConvertMaterial(material, context);

            return XmasContent.GetInstance().GetSkinningMaterial(directory, material, context);
        }

    [DisplayName("Normal Map Texture")]
    [Description("If set, this file will be used as the normal map on the model, " +
    "overriding anything found in the opaque data.")]
    [DefaultValue("")]
    public string NormalMapTexture
    {
      get { return normalMapTexture; }
      set { normalMapTexture = value; }
    }
    private string normalMapTexture;

    [DisplayName("Specular Map Texture")]
    [Description("If set, this file will be used as the specular map on the model, " +
    "overriding anything found in the opaque data.")]
    [DefaultValue("")]
    public string SpecularMapTexture
    {
      get { return specularMapTexture; }
      set { specularMapTexture = value; }
    }
    private string specularMapTexture;

    [DisplayName("Height Map Texture")]
    [Description("If set, this file will be used as the height map on the model, " +
    "overriding anything found in the opaque data.")]
    [DefaultValue("")]
    [Browsable(true)]
    public string HeightMapTexture
    {
      get { return heightMapTexture; }
      set { heightMapTexture = value; }
    }
    private string heightMapTexture;
  }
}
